#ifndef MEMBLOCK_H
#define MEMBLOCK_H

/** @file memblock.h
* Allocates a block of memory and returns chunks of that block. When the block
* is depleted, a new block is allocated and linked together with the previous
* block of memory. Strings are kept in a special block which is expanded
* dynamically. The information about these chains of MemBlocks are kept in a
* MemBlockInfo data structure. Using MemBlock dramatically reduces the number 
* of calls to malloc() and free().
*
* @b Example:
* @code
*     struct MemBlockInfo* mem=NULL;    //memory blocks descriptor
*     struct Node* node1=NULL;          //sample element to allocate
*     struct Node* node2=NULL;          //another element to allocate
*
*     //Start a new chain of MemBlocks with default # of elements per block
*     mem=memInit(sizeof(struct Node));
*
*     node1=memGet(mem);                //get a new struct Node*
*     node2=memGet(mem);                //get another struct Node*
*
*     memFree(mem);                     //free all memory
* @endcode
*
* @author Victor Petrov <victor_petrov@harvard.edu>
* @date   Apr 02, 2011
* @cond
*------------------------------------------------------------------------------
* LEGAL:
*------------------------------------------------------------------------------
*
* Copyright (c) 2011, The President and Fellows of Harvard College
* Copyright (c) 2011, The Neuroinformatics Research Group at Harvard University
* All rights reserved.
*
* License: New BSD License   <http://creativecommons.org/licenses/BSD/>
*-------------------------
* Redistribution and use in source and binary forms, with or without 
* modification, are permitted provided that the following conditions are met:
*
*	Redistributions of source code must retain the above copyright notice, 
*	this list of conditions and the following disclaimer.
*
*	Redistributions in binary form must reproduce the above copyright notice, 
*	this list of conditions and the following disclaimer in the documentation
*	and/or other materials provided with the distribution.
*
*	Neither the name of the Neuroinformatics Research Group at Harvard, nor 
*	the names of its contributors may be used to endorse or promote products 
*	derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
* POSSIBILITY OF SUCH DAMAGE.
*------------------------------------------------------------------------------
* vim: ts=4
*------------------------------------------------------------------------------
* @endcond
*/

#include "stdlib.h"             /* size_t */

/** Stores information about a chain of MemBlock data structures. */
struct MemBlockInfo
{
    unsigned int blockCount;    /**< total number of blocks */
    size_t elemSize;            /**< size of 1 element to allocate */
    unsigned int elemTotal;     /**< total number of elements allocated */
    unsigned int elemPerBlock;  /**< total number of elements per MemBlock */

    char** strings;             /**< special growable space for strings */
    unsigned int stringsMax;    /**< max # of strings before next realloc() */
    unsigned int stringCount;   /**< current number of strings allocated */

    struct MemBlock* first;     /**< first MemBlock */
    struct MemBlock* last;      /**< last MemBlock */
};

/** Encapsulates an allocated buffer and stores metadata about it */
struct MemBlock
{
    struct MemBlockInfo* info;  /**< pointer to the info struct */
    unsigned int allocated;     /**< number of elements allocated */

    void* data;                 /**< internal buffer */

    struct MemBlock* previous;  /**< previous MemBlock */
    struct MemBlock* next;      /**< next MemBlock */
};

/** Initializes a new chain of MemBlocks.
* @note memInit() calls memInitEx() with a default number of elements defined
*       by GROW_BY. If you'd like a different number of
*       elements per MemBlock, use memInitEx() instead.
* @note memInit() needs to be called only once per group of elements.
*       A call to memFree() will free all the elements in the MemBlock chain
*       described by MemBlockInfo. 
* @note The MemBlock struct is zero-filled, which means all elements returned
*       by memGet() will be already initialized with zeros.
* @param[in] size The size of an element to be allocated by memGet()
* @return On Success, a MemBlockInfo struct used to manage the new MemBlocks or
*         @c NULL on Failure.
* @see memInit()
* @see memGet()
*/
struct MemBlockInfo* memInit(size_t size);

/** Initializes a new chain of MemBlocks with a custom number of elements per
* block.
* @note memInit() needs to be called only once per group of elements.
*       A call to memFree() will free all the elements in the MemBlock chain
*       described by MemBlockInfo. 
* @note The MemBlock struct is zero-filled, which means all elements returned
*       by memGet() will be already initialized with zeros.
* @param[in] size    The size of an element to be allocated by memGet()
* @param[in] grow_by The number of elements in each MemBlock
* @return On Success, a MemBlockInfo struct used to manage the new MemBlocks or
*         @c NULL on Failure.
* @see memInit()
* @see memGet()
*/
struct MemBlockInfo* memInitEx(size_t size, unsigned int grow_by);

/** Reserves a new element. This function does @b not use @c malloc() on
* every call. Instead, it uses a MemBlock to compute the address of the next
* available element. If the MemBlock is full, it will allocate a new MemBlock.
* @note The size of the elements allocated by memGet() is always the same.
*       It must be specified as the first parameter of memInit() or memInitEx()
* @warning Just like @c malloc(), memGet() will return @c NULL if it cannot
*          allocate more memory.
* @return A pointer to a new element on Success, @c NULL on Failure
* @see memInit()
* @see memInitEx()
*/
void* memGet(struct MemBlockInfo* info);

/** Frees all memory associated with a MemBlockInfo. This includes the list of
* MemBlocks and the @c strings buffer.
* @param[in] info The MemBlockInfo to free
* @warning calls @c free()
*/
void memFree(struct MemBlockInfo* info);

/** @name String Functions 
* @{
*/

/** Duplicates a string into the MemBlockInfo string buffer.The string will be 
* free'd together with the MemBlocks when memFree() is called.
* @note If the length of the string is already known, use memNewStringEx()
* @param[in] s     A NULL-terminated string
* @param[in] info  The MemBockInfo struct which will hold the new string data.
* @return A pointer to the new data on Success or @c NULL on Failure
* @note memNewString() is similar in intent to @c strdup()
* @see memString()
* @see memStringEx()
*/
char* memNewString(const char* s, struct MemBlockInfo* info);

/** Duplicates a string into the MemBlockInfo string buffer.The string will be 
* free'd together with the MemBlocks when memFree() is called. 
* @note If the length of the string is @c not known, use memNewString() will
*       call strlen() for you.
* @warning The value of @c length should be the same as if @c strlen(s) were
*          called, i.e. the NULL character should not be included.
* @param[in] s      A NULL-terminated string
* @param[in] len    The length of the string, @b not including the NULL char
* @param[in] info   The MemBockInfo struct which will hold the new string data.
* @return A pointer to the new data on Success or @c NULL on Failure
* @note memNewStringEx() is similar in intent to @c strndup()
* @see memString()
* @see memStringEx()
*/
char* memNewStringEx(const char* s, size_t len, struct MemBlockInfo* info);

/** Allocates memory for a new string. Grows the string container if necessary.
* @note uses @c malloc()
* @param[in] length  Number of chars, @c including the NULL character
* @param[in] info    The MemBlockInfo struct that will hold the new string data
* @return A pointer to the new data (@b not zero-filled)
* @see memStringEx()
*/
char* memString(size_t length, struct MemBlockInfo* info);

/** Allocates memory for a new string. Grows the string container if necessary.
* @note uses @c calloc()
* @param[in] length  Number of chars, @c including the NULL character
* @param[in] info    The MemBlockInfo struct that will hold the new string data
* @return A pointer to the new data (zero-filled)
* @see memString()
*/
char* memStringEx(size_t length, struct MemBlockInfo* info);
/**@}*/


#endif
